package fun.codedesign.yinxue.code.php2java.service.visitor;

import fun.codedesign.yinxue.code.engine.antlr.php.PhpParser;
import fun.codedesign.yinxue.util.ReflectUtil;
import fun.codedesign.yinxue.util.ResourceUtil;
import fun.codedesign.yinxue.util.StringUtil;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStreamRewriter;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;

import java.util.*;

/**
 * 使用修改tokens流 + 语法修正的方式
 * 好处：保留了相应的注释，并且可以插入相应的原始代码在注释当中
 * 问题：
 */
public class Php2JavaTokensVisitor extends Php2JavaStringBuilderVisitor {

    private Token useTokenStop;

    public Php2JavaTokensVisitor(TokenStreamRewriter rewriter) {
        super(rewriter);
    }

    @Override
    public Object visitUseDeclaration(PhpParser.UseDeclarationContext ctx) {
        // use -> import
        writer.replace(ctx.start, "import");
        List<TerminalNode> nodes = getTokens(ctx, PhpParser.Label);
        // 移除最后一个，因为最后是一个应该大写
        nodes.remove(nodes.size() - 1);
        for (TerminalNode node : nodes) {
            writer.replace(node.getSymbol(), node.getText().toLowerCase());
        }
        useTokenStop = ctx.stop;
        return super.visitUseDeclaration(ctx);
    }

    @Override
    public Object visitQualifiedNamespaceName(PhpParser.QualifiedNamespaceNameContext ctx) {
        if (PhpParser.NamespaceSeparator == ctx.start.getType()) {
            writer.replace(ctx.start, "");
            handleWordToLowerUnderNamespace(ctx, false);
        }
        // 将namespace的大写，拍成小写路径
        if (PhpParser.Namespace == ctx.start.getType()) {
            handleWordToLowerUnderNamespace(ctx, true);
        }
        if (PhpParser.Use == ctx.start.getType()) {

        }
        return super.visitQualifiedNamespaceName(ctx);
    }

    public void handleWordToLowerUnderNamespace(PhpParser.QualifiedNamespaceNameContext ctx, boolean lastWordNeedHandle) {
        // TODO优化
        List<TerminalNode> tokens = getTokens(getChildWithRec(ctx, PhpParser.NamespaceNameListContext.class, 0), PhpParser.Label);
        if (!lastWordNeedHandle) {
            tokens.remove(tokens.size() - 1);
        }
        for (TerminalNode token : tokens) {
            // 将路径小写化
            writer.replace(token.getSymbol(), context.namespaceChain.invokeRules(token.getText()));
        }
    }

    // TODO 获取下面的子项
    public <T extends ParseTree> T getChildWithRec(ParserRuleContext ctx, Class<? extends T> ctxType, int i) {
        if (ctx.children == null || i < 0 || i >= ctx.children.size()) {
            return null;
        }

        int j = -1; // what element have we found with ctxType?
        for (ParseTree o : ctx.children) {
            if (ctxType.isInstance(o)) {
                j++;
//                if (j == i) {
                return ctxType.cast(o);
//                }
            } else {
                if (!(o instanceof TerminalNodeImpl)) {
                    return getChildWithRec((ParserRuleContext) o, ctxType, i);
                }

            }
        }
        return null;
    }

    public List<TerminalNode> getTokens(ParserRuleContext ctx, int ttype) {
        if (ctx.children == null) {
            return Collections.emptyList();
        }

        List<TerminalNode> tokens = new ArrayList<TerminalNode>();
        for (ParseTree o : ctx.children) {
            if (o instanceof TerminalNode) {
                TerminalNode tnode = (TerminalNode) o;
                Token symbol = tnode.getSymbol();
                if (symbol.getType() == ttype) {
                    tokens.add(tnode);
                }
            } else {
                List<TerminalNode> tokens2 = getTokens((ParserRuleContext) o, ttype);
                tokens.addAll(tokens2);
            }
        }

        if (tokens == null) {
            return Collections.emptyList();
        }
        return tokens;
    }

    @Override
    public Object visitDeclareStatement(PhpParser.DeclareStatementContext ctx) {
        // declare(strict_types=1);语句清除
        writer.replace(ctx.start, ctx.stop, "");
        return super.visitDeclareStatement(ctx);
    }

    @Override
    public Object visitNamespaceNameList(PhpParser.NamespaceNameListContext ctx) {
        // / -> .
        List<TerminalNode> tokens = ctx.getTokens(PhpParser.NamespaceSeparator);
        for (TerminalNode token : tokens) {
            writer.replace(token.getSymbol(), ".");
        }
        return super.visitNamespaceNameList(ctx);
    }

    @Override
    public Object visitClassDeclaration(PhpParser.ClassDeclarationContext ctx) {
        // 默认添加 public
        writer.insertBefore(ctx.start, "public ");
        // 类名首字母大写 manager -> Manager
        Token className = ctx.identifier().start;
        writer.replace(className, StringUtil.toUpperCaseFirstChar(className.getText()));
        return super.visitClassDeclaration(ctx);
    }

    @Override
    public Object visitVariableInitializer(PhpParser.VariableInitializerContext ctx) {
        Token var = ctx.VarName().getSymbol();
        // $ 驼峰 ...  非入参的情况下采用增加 Object 声明类型
        if (!(ctx.parent instanceof PhpParser.FormalParameterContext)) {
            writer.replace(var, "Object " + context.wordChain.invokeRules(var.getText()));
        }
        return super.visitVariableInitializer(ctx);
    }

    @Override
    public Object visitKeyedVariable(PhpParser.KeyedVariableContext ctx) {
        Token var = ctx.VarName().getSymbol();
        writer.replace(var, context.wordChain.invokeRules(var.getText()));
        return super.visitKeyedVariable(ctx);
    }

    @Override
    public Object visitClassStatement(PhpParser.ClassStatementContext ctx) {
        String text = writer.getText("default", Interval.of(ctx.start.getTokenIndex(), ctx.stop.getTokenIndex()));
        // 在生成的文件里，放置相应的原始代码，并且注释
        writer.insertBefore(ctx.start, "/**\r\n" + text + "\r\n*/\r\n");
        // use语句
        if (PhpParser.Use == ctx.start.getType()) {
            ParserRuleContext namespace = getChildWithRec(ctx, PhpParser.NamespaceNameListContext.class, 0);
            String packageImport = context.importChain.invokeRules(namespace.getText());
            writer.insertAfter(useTokenStop, "\nimport " + packageImport + ";");
//            TODO 不能使用范围替换
//            writer.replace(namespace.start, namespace.stop, "");
        }

        // 构造器替换
        Object ret = defaultResult();
        PhpParser.IdentifierContext identifier = ctx.identifier();
        if (identifier == null) {
            super.visitClassStatement(ctx);
            return ret;
        }

        Token methodName = identifier.start;
        int type = methodName.getType();
        TerminalNode functionNode = ctx.getToken(PhpParser.Function_, 0);
        if (PhpParser.Constructor == type) {
            // 将function替换为空
            writer.replace(functionNode.getSymbol(), "");
        } else if (PhpParser.Label == type) {
            // 驼峰
            writer.replace(methodName, context.wordChain.invokeRules(methodName.getText()));
            // 如果有return节点，就按照Object返回
            // 遍历token，查找有没return节点
            // TODO 封装成一个方法吗 提高性能吗
            List<Token> tokens = ((CommonTokenStream) writer.getTokenStream()).getTokens(ctx.start.getTokenIndex(), ctx.stop.getTokenIndex());
            boolean returnFlag = false;
            for (Token token : tokens) {
                if (PhpParser.Return == token.getType()) {
                    returnFlag = true;
                }
            }
            if (returnFlag) {
                // TODO 根据返回值能优化吗
                // 如果有子节点
                PhpParser.BaseCtorCallContext ctorCallContext = getChildWithRec(ctx, PhpParser.BaseCtorCallContext.class, 0);
                if (ctorCallContext != null) {
                    writer.replace(functionNode.getSymbol(), ctorCallContext.stop.getText());
                    // 清除该后置节点 method() :AbstractAnnotation
                    writer.replace(ctorCallContext.start, "");
                    writer.replace(ctorCallContext.stop, "");
                } else {
                    writer.replace(functionNode.getSymbol(), "Object");
                }
            } else {
                writer.replace(functionNode.getSymbol(), "void");
            }
        }
        return super.visitClassStatement(ctx);
    }

    @Override
    public Object visitString(PhpParser.StringContext ctx) {
        // TODO
        writer.replace(ctx.start, ctx.start.getText().replace("'", "\""));
        return super.visitString(ctx);
    }

    @Override
    public Object visitForeachStatement(PhpParser.ForeachStatementContext ctx) {
        // foreach 括号中 as 前后需要对调
        // 能将 as 前后的上下文对调吗
//        ParseTree temp = ctx.getChild(2);
//        ctx.children.set(2, ctx.getChild(4));
//        ctx.children.set(4, temp);

        return super.visitForeachStatement(ctx);
    }

    @Override
    public Object visitFormalParameter(PhpParser.FormalParameterContext ctx) {
        // 如果没有声明类型，就默认Object，有的话就不用处理
        if (ctx.getChildCount() == 1) {
            writer.replace(ctx.start, "Object " + context.wordChain.invokeRules(ctx.start.getText()));
        } else {
            // 普通类型如 string 给配置表更换
            PhpParser.TypeHintContext typeHintContext = ctx.typeHint();
            PhpParser.VariableInitializerContext variableInitializerContext = ctx.variableInitializer();
            writer.replace(variableInitializerContext.start, context.wordChain.invokeRules(variableInitializerContext.start.getText()));
        }
        return super.visitFormalParameter(ctx);
    }

    @Override
    public Object visitKeyedSimpleFieldName(PhpParser.KeyedSimpleFieldNameContext ctx) {
        Token token = ctx.start;
        writer.replace(token, context.wordChain.invokeRules(token.getText()));
        return super.visitKeyedSimpleFieldName(ctx);
    }

    @Override
    public Object visitInnerStatement(PhpParser.InnerStatementContext ctx) {
        return super.visitInnerStatement(ctx);
    }

    @Override
    public Object visitTerminal(TerminalNode node) {
        Integer type = node.getSymbol().getType();
        if (map.containsKey(type)) {
            String exr = map.get(type);
            if (exr.startsWith("$")) {
                // 将取值赋给value
                String var = exr.replace("$", "");
                exr = (String) ReflectUtil.getFieldValue(context.javaDesc, var);
            }
            writer.replace(node.getSymbol(), exr);
        }
        return super.visitTerminal(node);
    }

    static Map<Integer, String> map = new HashMap<>();

    static {
        String text = ResourceUtil.getResourceAsText("rule/php2java.rule");
        String[] lines = text.split("\r\n");
        for (String line : lines) {
            // 注释跳过
            if (line.startsWith("#")) {
                continue;
            }
            String[] vars = line.split("=");
            try {
                map.put(Integer.parseInt(vars[0]), vars[1]);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
